﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace JRBase
{
    public class Map : IGameObject
    {
        protected int width;
        protected int height;
        protected int screenWidth;
        protected int screenHeight;
        private Rectangle window;
        private int relX;
        private int relY;
        private float graffity;

        MapSprite backGround;

        protected Actor player;
        protected List<Actor> enemies;
        protected List<Actor> boxes;
        protected List<Actor> collidedEnemies;

        protected IMapHandler mapHandler;

        public int Width
        {
            get { return width; }
        }

        public int Height
        {
            get { return height; }
        }

        public List<Actor> Enemies
        {
            get { return enemies; }
        }

        public List<Actor> Boxes
        {
            get { return boxes; }
        }

        public List<Actor> CollidedEnemies
        {
            get { return collidedEnemies; }
        }

        public Actor Player
        {
            get { return player; }
        }

        public void Initialize(Texture2D texture, Actor player,
            int screenWidth, int screenHeight, int levelWidth, int levelHeight,
            int relX, int relY, float graffity, IMapHandler mapHandler)
        {
            this.player = player;
            this.screenWidth = screenWidth;
            this.screenHeight = screenHeight;
            this.width = levelWidth;
            this.height = levelHeight;
            this.relX = relX;
            this.relY = relY;
            this.mapHandler = mapHandler;

            enemies = new List<Actor>();
            boxes = new List<Actor>();
            collidedEnemies = new List<Actor>();

            backGround = new MapSprite();
            backGround.Initialize(texture);
            this.graffity = graffity;
        }

        public void Update(GameTime gameTime)
        {
            calculateWindow();
            player.Update(gameTime, calculatePlayerDrawPos(player));
            player.Y = (int)calculateGraffity(player.Weight, player.Y);
            foreach (Actor enemy in enemies)
            {
                if (isActorVisible(enemy))
                {
                    enemy.Update(gameTime, calculateActorDrawPos(enemy));
                    if (player.collidesWith(enemy))
                    {
                        collidedEnemies.Add(enemy);
                        //player.keepOutOf(enemy);
                    }
                }
            }
            bool collWithBox = false;
            foreach (Actor box in boxes)
            {
                if (isActorVisible(box))
                {
                    box.Update(gameTime, calculateActorDrawPos(box));
                    if (player.collidesWith(box))
                    {
                        collWithBox = true;
                        player.keepOutOf(box);
                    }
                }
            }
            if (!collWithBox)
            {
                player.OnBottom = false;
            }
            keepActorInMap(player);

            mapHandler.Update(gameTime);
        }

        private float calculateGraffity(float weight, float Y)
        {
            return Y + (weight * graffity);
        }

        private bool isActorVisible(Actor actor)
        {
            return window.Intersects(actor.Rectangle);
        }

        private void keepActorInMap(Actor actor)
        {
            actor.X = (int)MathHelper.Clamp(actor.X, 0, this.width - actor.Rectangle.Width);
            actor.Y = (int)MathHelper.Clamp(actor.Y, 0, this.height - actor.Rectangle.Height);
        }

        private void calculateWindow()
        {
            int x = ((int)player.Rectangle.X + player.Rectangle.Width / 2) - (screenWidth / 2) - (int)relX;
            int y = ((int)player.Rectangle.Y + player.Rectangle.Height / 2) - (screenHeight / 2) - (int)relY;

            x = (int)MathHelper.Clamp(x, 0, width - screenWidth);
            y = (int)MathHelper.Clamp(y, 0, height - screenHeight);

            window = new Rectangle(x, y, screenWidth, screenHeight);
        }

        private Vector2 calculateActorDrawPos(Actor actor)
        {
            return new Vector2(actor.Rectangle.X + actor.Rectangle.Width / 2 - window.X,
                actor.Rectangle.Y + actor.Rectangle.Height / 2 - window.Y);
        }

        private Vector2 calculatePlayerDrawPos(Actor actor)
        {
            Vector2 actorVec = new Vector2(
                actor.Rectangle.X + actor.Rectangle.Width / 2,
                actor.Rectangle.Y + actor.Rectangle.Height / 2);

            int actorX = (screenWidth / 2) + relX;
            int actorY = (screenHeight / 2) + relY;

            if (actorVec.X < (screenWidth / 2) + relX)
            {
                actorX = (int)actorVec.X;
            }
            if (actorVec.X > width - (screenWidth / 2) + relX)
            {
                actorX = (int)(actorVec.X - (width - screenWidth));
            }

            if (actorVec.Y < (screenHeight / 2) + relY)
            {
                actorY = (int)actorVec.Y;
            }
            if (actorVec.Y > height - (screenHeight / 2) + relY)
            {
                actorY = (int)(actorVec.Y - (height - screenHeight));
            }

            return new Vector2(actorX, actorY);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            backGround.Window = window;
            backGround.Draw(spriteBatch);
            player.Draw(spriteBatch);
            foreach (Actor box in boxes)
            {
                if (isActorVisible(box))
                {
                    box.Draw(spriteBatch);
                }
            }
            foreach (Actor enemy in enemies)
            {
                if (isActorVisible(enemy))
                {
                    enemy.Draw(spriteBatch);
                }
            }
        }
    }
}
